#include<cstring>
#include"Socket.hpp"
#include"log.hpp"

#define SIZE 1024 //缓冲区大小
const int deafult_num = -1;
class EpollServer{
    public:
        EpollServer(uint16_t port)
            :_port(port)
            ,_listen_socket(new TcpSocket(_port))
            ,_epoll_fd(-1)    
        {}
        void Init()
        {
            _listen_socket->Build_Socket(_port);
            _epoll_fd = epoll_create(1024);//创建epoll句柄
            if(_epoll_fd < 0){
                LOG(log_level::ERROR)<<"epoll_create error";
                exit(1);
            }
            LOG(log_level::INFO)<<"epoll_create success: "<<_epoll_fd;

            //把listenfd添加到epoll中
            struct epoll_event ev;
            ev.events = EPOLLIN;//监听sockfd只读
            ev.data.fd = _listen_socket->Fd();
            int n = epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, _listen_socket->Fd(), &ev);
            if(n < 0){
                LOG(log_level::ERROR)<<"epoll_ctl error";
                exit(1);
            }
        }
        void Loop()
        {
            // int timeout = 1000;
            int timeout = -1;
            _is_running = true;
            while(_is_running){
                int n = epoll_wait(_epoll_fd, _events, SIZE, timeout);//返回就绪的事件数
                switch(n)
                {
                    case 0:
                        std::cout<<"time out"<<std::endl;
                        break;
                    case -1:
                        perror("poll wait error");
                        break;
                    default://内核告诉用户有fd就绪(可能是监听sockfd获取新连接，也可能是其他合法fd数据准备好了),处理事件
                        std::cout<<"new connection "<<std::endl;
                        Dispatch(n);
                        break;
                }
            }
            _is_running = false;
        }
        void Accept()//listensockfd获取新连接成功，直接调用accept
        {
            struct sockaddr_in client_addr;
            int new_sockfd = _listen_socket->Accept((struct sockaddr*)&client_addr);//不会阻塞
            if(new_sockfd < 0){
                return;
            }else{//新连接
                std::string client_ip = inet_ntoa(client_addr.sin_addr);//获取客户端ip
                uint16_t client_port = ntohs(client_addr.sin_port);//获取客户端端口
                LOG(log_level::INFO)<<"new connection from "<<client_ip<<":"<<client_port;
            }
            struct epoll_event ev;//新fd所关心的事件
            ev.events = EPOLLIN;
            ev.data.fd = new_sockfd;
            int n = epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, new_sockfd, &ev);
            if(n < 0){
                LOG(log_level::ERROR)<<"epoll_ctl error";
                close(new_sockfd);
            }
            LOG(log_level::INFO)<<"add new connection to epoll "<<new_sockfd;
            
        }
        void DealData(int sockfd)//处理IO数据
        {
            //只是写一个demo,数据的处理想要完备，必须引入协议，太麻烦了
            char buffer[1024];
            ssize_t n = recv(sockfd, buffer, sizeof(buffer)-1, 0);//不会阻塞
            if(n>0){
                buffer[n] = '\0';
                std::cout<<"client say: "<<buffer<<std::endl;
                send(sockfd, buffer, n, 0);//发是可能会阻塞的，因为只处理了读的逻辑，用poll,epoll写这份代码时会处理
                
            }else if(n==0){
                LOG(log_level::INFO)<<"client quit ";
                int m = epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, sockfd, nullptr);//删除epoll红黑树的节点
                if(m < 0){
                    LOG(log_level::ERROR)<<"epoll_ctl error";
                    return;
                }
                LOG(log_level::INFO)<<"remove connection from epoll "<<sockfd;
                close(sockfd);
                
            }else{
                LOG(log_level::ERROR)<<"recv error ";
                int m = epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, sockfd, nullptr);
                if(m < 0){
                    LOG(log_level::ERROR)<<"epoll_ctl error";
                    return;
                }
                close(sockfd);
            }
        }
        void Dispatch(int n)
        {
            for(int i = 0; i < n; i++){
                uint32_t events = _events[i].events;
                int fd = _events[i].data.fd;
                if(fd == _listen_socket->Fd() && (events & EPOLLIN)){
                    Accept();
                }else if(events & EPOLLIN){//读事件就绪
                    DealData(fd);
                }else if(events & EPOLLOUT){//写事件就绪
                //...
                }
            }
        }
        ~EpollServer()
        {
            _listen_socket->Close();
            if(_epoll_fd >= 0)
                close(_epoll_fd);
        }
    private:
        uint16_t _port;
        std::unique_ptr<Socket> _listen_socket;
        bool _is_running = false;
        int _epoll_fd;
        struct epoll_event _events[SIZE];//epoll就绪事件数组
};